#include "Bundle.h"
#include "stdlib.h"
#include "Util.h"
#include <exception>
#include "Bundles.h"
#include "Policy.h"
#include "XMLTree.h"
#include <time.h>
#include "HBSD.h"
#include "GBOF.h"
#include "Logging.h"
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/sax2/Attributes.hpp>

using namespace std;


Bundle::Bundle(Bundles *manager)
{
	routingInfo = false;
	injected = false;
	localId = -1;
	bytesReceived = 0;
	numMetaBlocks = 0;
	expiration = 0;
	creationTimestamp = 0;
	creationSeconds = 0;
	expiresAtMillis = 0; // Not set for injected & delivery bundles!
	fragLength = 0;
	fragOffset = 0;
	isFragment = false;
	bundleManager = manager;
}

Bundle::~Bundle()
{
	bundleManager = NULL;
}

long Bundle::initReceived(XMLTree* evtBundleRcvd) 
{
try {
	// This bundle was received, not injected.
	// Get mandatory attributes. Convert them to their integer/long
	// value. (Java doesn't support unsigned ints so we may use longs to
	// store some of the unsigned values.)
	string strLocalId = evtBundleRcvd->getAttrRequired(string("local_id"));
	localId = atol(strLocalId.c_str());

	string strExpiration = evtBundleRcvd->getAttrRequired(string("expiration"));
	expiration = atol(strExpiration.c_str());

	string strBytesReceived = evtBundleRcvd->getAttrRequired(string("bytes_received"));
	bytesReceived = atoi(strBytesReceived.c_str());

	// Optional attributes.
	if (evtBundleRcvd->haveAttr(string("num_meta_blocks"))) 
	{
		string strNumMetaBlocks = evtBundleRcvd->getAttr(string("num_meta_blocks"));
		numMetaBlocks = atoi(strNumMetaBlocks.c_str());
	}

	// Now parse the child elements.
	int nElements = evtBundleRcvd->numChildElements();
	for (int x=0; x<nElements; x++) 
	{
		XMLTree *el = evtBundleRcvd->getChildElement(x);
		if (el->getName().compare("gbof_id") == 0) 
		{
			parseGBOF(el);
		} else if (el->getName().compare("dest") == 0) 
		{
			destURI = el->getAttrRequired(string("uri"));
			destEID = GBOF::eidFromURI(destURI);
		} else if (el->getName().compare("custodian") == 0) 
		{
			custodianURI = el->getAttrRequired(string("uri"));
		} else if (el->getName().compare("replyto") == 0) 
		{
			replyToURI = el->getAttrRequired(string("uri"));
		} else if (el->getName().compare("prevhop") == 0) 
		{
			prevHopURI = el->getAttrRequired(string("uri"));
		}
	}
}
catch (exception &e) 
{
	if (HBSD::log->enabled(Logging::ERROR)) 
	{
		HBSD::log->error(string("Unanticipated exception initializing Bundle object: ") + string(e.what()));
	}
	return -1;
}

// Special case: the bundle is destined for this router. If that is the
// case then we should have already received a bundle_delivery_event for
// this bundle. So, after all that work, forget about it.

if (destURI.compare(HBSD::hbsdRegistration) == 0) 
{
	return -1;
}

expiresAtMillis = GBOF::calculateExpiration(creationTimestamp, expiration);
if (HBSD::log->enabled(Logging::DEBUG)) 
{
	// in seconds
	long xx = (expiresAtMillis / 1000) - time(NULL);

	//HBSD::log->debug(string("Created Bundle object:") + string(" localId=") + localId + string(" expiration=") + expiration + string(" (") + to_string(xx) + string(" seconds)") + string(" sourceURI=") + sourceURI + string(" destURI=") + destURI);
}

return localId;

}



long Bundle::initFromReport(XMLTree* event) 
{
	try 
	{
		// Create the bundle from the information in the report. The global
		// creation timestamp and local Id need to be derived from the creation
		// seconds and sequence Ids.
		long localSeq = atol(event->getAttrRequired(string("bundleid")).c_str());

		long globalSeq = atol(event->getAttrRequired(string("creation_ts_seqno")).c_str());

		long creationSecs = atol(event->getAttrRequired(string("creation_ts_seconds")).c_str());
		localId = creationSecs << 32 | localSeq;

		creationTimestamp = creationSecs << 32 | globalSeq;

		creationSeconds = GBOF::creationSeconds(creationTimestamp);

		expiration = atol(event->getAttrRequired(string("expiration")).c_str());

		expiresAtMillis = GBOF::calculateExpiration(creationTimestamp, expiration);

		fragLength = atoi(event->getAttrRequired(string("orig_length")).c_str());

		fragOffset = atoi(event->getAttrRequired(string("frag_offset")).c_str());

		isFragment = (bool)atoi(event->getAttrRequired(string("is_fragment")).c_str());

		int nElements = event->numChildElements();

		for (int x=0; x<nElements; x++) 
		{
			XMLTree *el = event->getChildElement(x);
			if ((el->getName()).compare("gbof_id") == 0) 
			{
				parseGBOF(el);
			} else if ((el->getName()).compare("dest") == 0) 
			{
				destURI = el->getAttrRequired(string("uri"));
				if (destURI.compare(HBSD::hbsdRegistration) == 0) 
				{
					// Old registration meta-data bundle.
					return -1;
				}
				destEID = GBOF::eidFromURI(destURI);
			} else if ((el->getName()).compare("custodian") ==0) 
				{
					custodianURI = el->getAttrRequired(string("uri"));
				} else if (el->getName().compare("replyto") == 0) 
					{
						replyToURI = el->getAttrRequired(string("uri"));
					} else if (el->getName().compare("prevhop") == 0) {
						prevHopURI = el->getAttrRequired(string("uri"));
					} else if (el->getName().compare("source") == 0) {
						sourceURI = el->getAttrRequired(string("uri"));
						sourceEID = GBOF::eidFromURI(sourceURI);
					}
		}
	
		sourceEID = GBOF::eidFromURI(sourceURI);
		// Well, after all of that work we decide if we want to discard
		// this bundle. That's because the local_id is bogus if the
		// bundle was not locally created. It seems that the local_id uses
		// the timestamp from when this node received the bundle, as opposed
		// to the global creation timestamp.
		if (!sourceEID.compare(HBSD::localEID) == 0) 
		{
			if (HBSD::log->enabled(Logging::DEBUG)) 
			{
				HBSD::log->debug(string("Bundle in report ignored (not locally created): ")+ Util::to_string(creationTimestamp));
			}
			return -1;
		}
		gbofKey = GBOF::keyFromBundleObject(this);
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			long xx = (expiresAtMillis/1000) - time(NULL);
			
			//HBSD::log->debug(string("Created Bundle object from report:") + string(" localId=") + string(localId) + string(" expiration=") + to_string(expiration) + string(" (") + to_string(xx) + string(" seconds)") + string(" sourceURI=") + string(sourceURI) + string(" destURI=") + string(destURI));
			
		}
		return localId;
	} 
	catch (exception& e) 
	{
		return -1;
	}

}


long Bundle::initGeneric(XMLTree* event) 
{
try 
{
	// Get the local id of the bundle.
	string strLocalId = event->getAttrRequired(string("local_id"));
	localId = atol(strLocalId.c_str());
	// Now parse the GBOF.
	XMLTree *el = event->getChildElementRequired(string("gbof_id"));
	parseGBOF(el);
	return localId;
} 


catch (exception &e) 
{
	if (HBSD::log->enabled(Logging::ERROR)) 
	{
		HBSD::log->error(string("Unanticipated exception initializing Bundle object: ") + string(e.what()));
	}
	return -1;
}
}

void Bundle::parseGBOF(XMLTree *element)  
{
	creationTimestamp = atol((element->getAttrRequired(string("creation_ts"))).c_str());
	creationSeconds = GBOF::creationSeconds(creationTimestamp);
	fragLength = atoi((element->getAttrRequired(string("frag_length"))).c_str());
	fragOffset = atoi((element->getAttrRequired(string("frag_offset"))).c_str());
	isFragment = (bool)atoi((element->getAttrRequired(string("is_fragment"))).c_str());
	XMLTree *el = element->getChildElementRequired(string("source"));
	sourceURI = el->getAttrRequired(string("uri"));
	sourceEID = GBOF::eidFromURI(sourceURI);
	gbofKey = GBOF::keyFromBundleObject(this);
}

bool Bundle::isCurrent() 
{
	return bundleManager->isCurrent(localId);
}
	
void Bundle::deleteBundle() 
{
	bundleManager->deleteBundle(this);
}

void Bundle::finished() 
{
	bundleManager->finished(this);
}
